home *** CD-ROM | disk | FTP | other *** search
/ Programming Sound Cards / Programming Sound Cards.iso / sound_56 / dmastp10.asm < prev    next >
Encoding:
Assembly Source File  |  1995-01-01  |  19.0 KB  |  504 lines

  1. ;══════════════════════════════════════════════════════════════════════════════
  2. ; Play with Cxh/Bxh commands on SoundBlaster 16/16ASP (here 16/mono/unsigned)
  3. ;   André Baresel (with some help from Craig Jackson)
  4. ;──────────────────────────────────────────────────────────────────────────────
  5. ; STATUS: DOES WORK ON SB16
  6. ; ■ sound crackles after a while - dounno yet what it is ...
  7. ; (somethings going wrong in CONVERT_HALF...)
  8. ;──────────────────────────────────────────────────────────────────────────────
  9. ; Requirements: 80286, SoundBlaster 16/16ASP (see BASEADDR,DMA channel,IRQ number)
  10. ; Resolutions : 16-bit / 4..44khz
  11. ; Note        : ■ We use only 8 bit data and convert it while playing into
  12. ;                 16bit, look at CONVERT_HALF (at the end of this file)
  13. ;               ■ To creat a 8 bit mono unsigned file do :
  14. ;                 "VOC2RAW TEST1.VOC /I"
  15. ;
  16. ; ■ DSP command 41h  ... set DAC sample rate
  17. ; ■ DSP command D0h  ... Halt Autoinit 8 bit DMA operation
  18. ; ■ DSP command D4h  ... Continue Autoinit 8 bit DMA operation
  19. ; ■ DSP command B6h 00h ... autoinit 16 bit mono data with no sign
  20. ;
  21.  
  22. .MODEL small
  23. .286
  24. ; CONSTANTS ───────────────────────────────────────────────────────────────────
  25.  
  26. ; SoundBlaster SETUP
  27. BASEADDR           EQU 0220h       ;SoundBlaster base address
  28. DMAchannel         EQU 5           ;SoundBlaster DMA channel
  29. IRQ7               EQU 15          ;SoundBlaster IRQ
  30.  
  31. ; PIC MASKS FOR MASK/DEMASK IRQ
  32. PICANDMASK         EQU 01111111b   ;AND PIC mask for clear IRQ
  33. PICORMASK          EQU 10000000b   ;OR PIC mask for set IRQ
  34.  
  35. ; DMA CONTROLLER REGISTERS (16bit)
  36. WRITEMASK          EQU 0D4h         ;WRITE MASK REGISTER
  37. WRITEMODE          EQU 0D6h         ;WRITE MODE REGISTER
  38. CLEARFLIPFLOP      EQU 0D8h
  39. PAGE16_CHN         EQU 08Bh         ;PAGE REGISTER FOR DMAchannel 5
  40. BASE16_CHN         EQU 0C4h         ;BASEADDRESS REGISTER DMA 5
  41. COUNT16_CHN        EQU 0C6h         ;COUNT REGISTER DMAchannel 5
  42.  
  43. ; SAMPLERATE :
  44. RATE               EQU 02AEDh       ; = 10989 Hz
  45.  
  46. ; DMA MODE
  47. WANTEDMODE         EQU 01011000b    ; singlemode, autoinit, readmode
  48.  
  49. ; DMABuffer size :
  50. DMABUFFERSIZE      EQU 8*1024
  51.  
  52. ;──────────────────────────────────────────────────────────────────────────────
  53. ; MACRO DEFINITIONs
  54. ;──────────────────────────────────────────────────────────────────────────────
  55. STARTUP                 MACRO
  56. ; MASM 5.x COMPATIBILITY
  57. __start:                mov     ax,DGROUP
  58.                         mov     ds,ax
  59.                         mov     bx,ss
  60.                         sub     bx,ax
  61.                         shl     bx,004h
  62.                         mov     ss,ax
  63.                         add     sp,bx
  64. ENDM
  65.  
  66. WAITWRITE               MACRO
  67. LOCAL                   loopWait,endloop
  68. ;          Arguments : DX = Status port (BASEADDR+0Ch)
  69. ;          Returns   : n/a
  70. ;          Destroys  : AL
  71.  
  72.                         push    cx
  73.                         xor     cx,cx           ; need that for slow SBs !
  74. loopWait:               dec     cx
  75.                         jz      endloop
  76.                         in      al,dx           ; AL = WRITE COMMAND STATUS
  77.                         or      al,al
  78.                         js      loopWait        ; Jump if bit7=1 - writing not allowed
  79. endloop:                pop     cx
  80. ENDM
  81.  
  82. WAITREAD                MACRO
  83. LOCAL                   loopWait,endloop
  84. ;          Arguments : DX = Status port   (normaly BASEADDR+0Eh)
  85. ;          Returns   : n/a
  86. ;          Destroys  : AL
  87.  
  88.                         push    cx
  89.                         xor     cx,cx           ; need that for slow SBs !
  90. loopWait:               dec     cx
  91.                         jz      endloop
  92.                         in      al,dx           ; AL = DATA AVAILABLE STATUS
  93.                         or      al,al
  94.                         jns     loopWait        ; Jump if bit7=0 - no data available
  95. endloop:                pop     cx
  96. ENDM
  97.  
  98. RESET_DSP               MACRO
  99. local                   SBthere
  100. ;          Arguments : n/a
  101. ;          Returns   : n/a
  102. ;          Destroys  : DX,AL
  103.  
  104.                         mov      dx,BASEADDR+06h
  105.                         mov      al,1
  106.                         out      dx,al          ; start DSP reset
  107.  
  108.                         in       al,dx
  109.                         in       al,dx
  110.                         in       al,dx
  111.                         in       al,dx          ; wait 3 µsec
  112.  
  113.                         xor      al,al
  114.                         out      dx,al          ; end DSP Reset
  115.  
  116.                         add      dx,08h         ; dx = DSP DATA AVAILABLE
  117.                         WAITREAD
  118.                         sub      dx,4           ; dx = DSP Read Data
  119.                         in       al,dx
  120.                         cmp      al,0aah        ; if there is a SB then it returns 0AAh
  121.                         je       SBthere
  122.                         jmp      RESET_ERROR    ; No SB - exit program
  123. SBthere:
  124. ENDM
  125. ;─── End of Macrodefinitions ──────────────────────────────────────────────────
  126.  
  127. .STACK 100h
  128.  
  129. .DATA
  130. ;──────────────────────────────────────────────────────────────────────────────
  131. ; Creat TEST1.INC with calling "VOC2RAW TEST1.VOC /I" or creat your own
  132. ; textfile with sampledata
  133. ;
  134. SAMPLEBUFFER LABEL BYTE
  135.     INCLUDE TEST1.INC
  136. SAMPLEBUFFEREND LABEL BYTE
  137.  
  138.     PART                db 0
  139.  
  140.     information         db 13,10,'DMASTP10.EXE - play 16bit mono data unsigned (that does only work on a'
  141.                         db 13,10,'SB16/SB16ASP)'
  142.                         db 13,10,'Pause playing with key "p" and continue it then with any key.'
  143.                         db 13,10,'Stop playing with <ESC>.',10,'$'
  144.     memerror            db 13,10,'Not enough memory to creat the DMA buffer','$'
  145.     txtpart0            db 13,'playing part 0','$'
  146.     txtpart1            db 13,'playing part 1','$'
  147.     sberror             db 13,10,'No SoundBlaster at this BASEADDR ! PROGRAM HALTED.','$'
  148.  
  149.     OLDInterruptSEG     dw ?
  150.     OLDInterruptOFS     dw ?
  151.  
  152.     ; OFFSET AND PAGE FOR DMAC
  153.     DMAbufferOFS        dw ?
  154.     DMAbufferPage       db ?
  155.  
  156.     ; OFFSET AND SEGMENT FOR CPU ACCESS :)
  157.     DMABufferDOSOFS     dw ?
  158.     DMABufferDOSSEG     dw ?
  159.  
  160.     ; POSITION IN SAMPLEBUFFER WHILE CONVERTING
  161.     position            dw 0
  162.  
  163.     SAMPLEBUFFERLENGTH = offset SAMPLEBUFFEREND - offset SAMPLEBUFFER
  164. ;──────────────────────────────────────────────────────────────────────────────
  165. .CODE
  166.  STARTUP
  167.            ; FIRST FREE NOT USED MEMORY :
  168.            mov     bx,ss
  169.            mov     ax,es
  170.            sub     bx,ax
  171.  
  172.            mov     ax,sp
  173.            add     ax,15
  174.  
  175.            shr     ax,4
  176.  
  177.            add     bx,ax
  178.            mov     ah,04ah
  179.            int     21h
  180.  
  181.            ; NOW ALLOCATE DMABUFFER
  182.            mov     bx,DMABUFFERSIZE*2/16       ; count of 16byte blocks for two buffers
  183.            mov     ah,48h
  184.            int     21h
  185.  
  186.            jnc     enoughmem
  187.            mov     dx,offset memerror
  188.            mov     ah,9
  189.            int     21h       ; WRITE MSG 2 SCRN THAT THERE'S NOT ENOUGH MEM
  190.            jmp     return2dos
  191. enoughmem: ; AX = segment of DMA buffer / offset = 0
  192.  
  193. ;──────────────────────────────────────────────────────────────────────────────
  194. ; calculate page and offset for 16bit DMAcontroller :
  195. ;
  196. ; segment*16+offset=20bit memory location-> upper 3 bits *2 = page
  197. ;                                           next 16 bits = offset
  198. ;                                           last 1 bit - lost in space :)
  199. ;                                           (because of word access)
  200. ;──────────────────────────────────────────────────────────────────────────────
  201.            rol     ax,4
  202.            mov     bl,al
  203.            and     bl,00eh
  204.            mov     [DMAbufferPage],bl
  205.            and     al,0f1h
  206.            ror     ax,1
  207.            mov     [DMABufferOFS],ax
  208. ;──────────────────────────────────────────────────────────────────────────────
  209. ; check for DMApage override :
  210. ; ... problem: DMA controller separates memory into 64KB pages, you can only
  211. ; transfer data is placed in one page - no page overrides are allowed
  212. ;──────────────────────────────────────────────────────────────────────────────
  213. ; To solve that :
  214. ; creat a DMA buffer with double size you want - if the first part is placed
  215. ; on a page border the second part is for sure not
  216. ;──────────────────────────────────────────────────────────────────────────────
  217.            mov     cx,DMABUFFERSIZE/2 ; we check for 128KB pages and DMABUFFERSIZE
  218.                                       ; in WORDs
  219.            neg     ax          ; ax = 65536 - ax   (bytes left to DMA page border)
  220.            cmp     ax,cx
  221.            ja      nooverride
  222.  
  223.            ; USE SECOND PART :
  224.            neg     ax               ; ax = offset first data
  225.            add     ax,cx            ; use second part
  226.            mov     [DMABufferOFS],ax
  227.            add     [DMABufferPage],2 ; 2nd part is on next page !
  228. nooverride:
  229.  
  230. ;──────────────────────────────────────────────────────────────────────────────
  231. ; now fill the  whole buffer with first words of data
  232. ; (2 times CALL CONVERT_HALF)
  233. ;
  234. ; but first - calculate the DOS SEG/OFS from the DMAPage/OFS (you know
  235. ; maybe we have to use second buffer half we don't know about ofs/seg yet)
  236.            mov     al,byte ptr [DMABufferOFS]
  237.            and     al,07h
  238.            xor     ah,ah
  239.            shl     ax,1
  240.            mov     di,ax       ; di = offset of DMAbuffer
  241.            mov     ax,[DMABufferOFS]
  242.            and     al,0f8h
  243.            mov     bl,[DMABufferPage]
  244.            shr     bl,1
  245.            or      al,bl
  246.            ror     ax,3
  247.            mov     es,ax       ; es = segment of DMABuffer
  248. ;──────────────────────────────────────────────────────────────────────────────
  249. ; save these values for later CONVERT_HALF calls
  250. ;
  251.            mov     [DMABufferDOSOFS],di
  252.            mov     [DMABufferDOSSEG],ax
  253.            xor     ax,ax
  254.  
  255.            ; DS:SI - samples in dataseg
  256.            ; ES:DI - DMABuffer
  257.  
  258.            ; fill the whole buffer with sample data
  259.            CALL    CONVERT_HALF
  260.            CALL    CONVERT_HALF
  261.  
  262.            ; NOW WE'RE READY FOR SB STUFF:
  263.  
  264.            RESET_DSP
  265.  
  266.            ; WRITE INFOMRATION TO SCREEN :
  267.            mov     dx,offset information
  268.            mov     ah,9
  269.            int     21h                  ; write program information to screen
  270.  
  271.            ; ENABLE SB SPEAKERS (for all SBs <SB16)
  272.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  273.            WAITWRITE
  274.            mov     al,0D1h                     ; AL = Enable speaker
  275.            out     dx,al                       ; Output: DSP Write Data or Command
  276.  
  277.            ; SETUP IRQ :
  278.            xor     ax,ax
  279.            mov     es,ax                       ; es to page 0 (Interrupt table)
  280.            mov     si,IRQ7*4                   ; si = position in interrupt table
  281.  
  282.            ; DISABLE IRQ (if it was enabled somehow)
  283.            in      al,021h
  284.            and     al,PICANDMASK               ; SET MASK REGISTER BIT TO DISABLE INTERRUPT
  285.            out     021h,al
  286.  
  287.            ; CHANGE POINTER IN INTERRUPT TABLE
  288.            mov     ax,es:[si]
  289.            mov     [OLDInterruptOFS],ax        ; save offset of old interupt vector for restoring
  290.            mov     ax,OFFSET OWN_IRQ
  291.            mov     es:[si],ax                  ; set offset of new interrupt routine
  292.            mov     ax,es:[si+2]
  293.            mov     [OLDInterruptSEG],ax        ; save segment of old interupt vector for restoring
  294.            mov     ax,cs
  295.            mov     es:[si+2],ax                ; set segment of new interrupt routine
  296.  
  297.            ; CHANGE PIC MASK :
  298.            in      al,021h
  299.            and     al,PICANDMASK   ; CLEAR MASK REGISTER BIT TO ENABLE INTERRUPT
  300.            out     021h,al
  301.  
  302.            mov     cx,DMABUFFERsize/2-1       ; count of words we need :)
  303. ;──────────────────────────────────────────────────────────────────────────────
  304. ; Setup DMA-controller :
  305. ;
  306. ; 1st  MASK DMA CHANNEL
  307. ;
  308.            mov     al,DMAchannel-4             ; channels 0..3 for the 2nd DMAC
  309.            add     al,4
  310.            out     WRITEMASK,al
  311. ;──────────────────────────────────────────────────────────────────────────────
  312. ; 2nd  CLEAR FLIPFLOP
  313. ;
  314.            out     CLEARFLIPFLOP,al
  315. ;──────────────────────────────────────────────────────────────────────────────
  316. ; 3rd  WRITE TRANSFER MODE
  317. ;
  318.            mov     al,WANTEDMODE
  319.            add     al,DMAchannel-4
  320.            out     WRITEMODE,al
  321. ;──────────────────────────────────────────────────────────────────────────────
  322. ; 4th  WRITE PAGE NUMBER
  323. ;
  324.            mov     al,[DMAbufferPage]
  325.            out     PAGE16_CHN,al
  326. ;──────────────────────────────────────────────────────────────────────────────
  327. ; 5th  WRITE BASEADDRESS
  328. ;
  329.            mov     ax,[DMABufferOFS]
  330.            out     BASE16_CHN,al
  331.            mov     al,ah
  332.            out     BASE16_CHN,al
  333. ;──────────────────────────────────────────────────────────────────────────────
  334. ; 6th  WRITE BUFFERLENGTH (in words)-1
  335. ;
  336.            mov     al,cl
  337.            out     COUNT16_CHN,al
  338.            mov     al,ch
  339.            out     COUNT16_CHN,al
  340. ;──────────────────────────────────────────────────────────────────────────────
  341. ; 7th  DEMASK CHANNEL
  342. ;
  343.            mov     al,DMAchannel-4
  344.            out     WRITEMASK,al
  345.  
  346. ;──────────────────────────────────────────────────────────────────────────────
  347. ; Setup SoundBlaster :
  348. ;
  349. ; 1st  SET SAMPLERATE
  350. ;
  351.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  352.            WAITWRITE
  353.            mov     al,041h                     ;AL = Set DAC Samplerate
  354.            out     dx,al
  355.            WAITWRITE
  356.            mov     cx,RATE
  357.            mov     al,ch
  358.            out     dx,al
  359.            WAITWRITE
  360.            mov     al,cl
  361.            out     dx,al
  362.  
  363. ;──────────────────────────────────────────────────────────────────────────────
  364. ; 2nd  USE 16bit MONO UNSIGNED MODE (DSPC B6h 00h)
  365. ;
  366.            WAITWRITE
  367.            mov     al,0B6h                     ;AL = DMA DAC 16bit autoinit
  368.            out     dx,al
  369.            WAITWRITE
  370.            mov     al,000h                     ;AL = mono unsigned data
  371.            out     dx,al
  372.            mov     cx,DMABUFFERSIZE/4-1
  373.            WAITWRITE
  374.            mov     al,cl                       ;AL = LOWER PART SAMPLELENGTH
  375.            out     dx,al
  376.            WAITWRITE
  377.            mov     al,ch                       ;AL = HIGHER PART SAMPLELENGTH
  378.            out     dx,al
  379.  
  380. ; TRANSFER STARTs.....NOW.... :)
  381.  
  382. waitloop:  mov     ah,01                       ;AH = Check for character function
  383.            int     016h                        ;   Interrupt: Keyboard
  384.            jz      waitloop                    ; wait for a key (sound in background)
  385.  
  386.            xor     ah,ah                       ;Read character, flush keypress
  387.            int     016h                        ;   Interrupt: Keyboard
  388.            cmp     al,'p'                      ; check for pause key
  389.            je      pause                       ; ok
  390.            cmp     al,27
  391.            jne     waitloop
  392.            jmp     exit
  393. pause:     ; NOW PAUSE PLAYING: (on DSPv4.04 you can also use DSPC d4h,d0h!)
  394.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  395.            WAITWRITE
  396.            mov     al,0D5h
  397.            out     dx,al
  398.  
  399.            ; WAIT FOR ANY KEY ("<ANY> key?" shut up it's a stupid joke!)
  400.            xor     ah,ah                       ;Read character, flush keypress
  401.            int     016h                        ;   Interrupt: Keyboard
  402.  
  403.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  404.            WAITWRITE
  405.            mov     al,0d6h
  406.            out     dx,al
  407.  
  408.            jmp     waitloop
  409.  
  410. exit:      RESET_DSP
  411.  
  412.            ; RESTORE PIC MASK
  413.            in      al,021h
  414.            or      al,PICORMASK                ;<-- SET REGISTER MASK BITS TO DISABLE
  415.            out     021h,al
  416.  
  417.            ; RESTORE IRQ :
  418.            xor     ax,ax
  419.            mov     es,ax                       ; es to page 0 (Interrupt table)
  420.            mov     si,IRQ7*4
  421.            mov     ax,[OLDInterruptOFS]
  422.            mov     es:[si],ax                  ; set old interrupt routine
  423.            mov     ax,[OLDInterruptSEG]
  424.            mov     es:[si+2],ax
  425.  
  426.            ; CLEAR KEYBUFFER
  427.            mov     ah,01
  428.            int     16h
  429.            jz      return2dos
  430.            xor     ah,ah                       ;Read character, flush keypress
  431.            int     016h                        ;   Interrupt: Keyboard
  432.  
  433.            ; TERMINATE EXE:
  434. return2dos:
  435.            mov     ax,04c00h
  436.            int     21h
  437.  
  438. ; display information if Soundblaster is not on this baseaddress
  439. RESET_ERROR:
  440.            mov     dx,offset sberror
  441.            mov     ah,9
  442.            int     21h                         ; text output
  443.            jmp     return2dos
  444.  
  445. ;──────────────────────────────────────────────────────────────────────────────
  446. ; Our own IRQ for detecting buffer half SB currently plays
  447. ; It's generated by the SoundBlaster hardware
  448. ;──────────────────────────────────────────────────────────────────────────────
  449. OWN_IRQ:   pusha
  450.            mov     dx,BASEADDR+00Fh            ;DX = IRQ ACKNOWLEDGE 16Bit
  451.            in      al,dx
  452.            mov     ax,@data
  453.            mov     ds,ax
  454.            mov     dx,offset txtpart0
  455.            cmp     [part],0
  456.            je      notpart1
  457.            mov     dx,offset txtpart1
  458. notpart1:  mov     ah,9
  459.            int     21h             ; text output
  460.            call    CONVERT_HALF    ; fill next half...
  461.            mov     al,020h
  462.            out     020h,al                     ;ACKNOWLEDGE HARDWARE INTERRUPT
  463.            popa
  464.            IRET
  465.  
  466. ;──────────────────────────────────────────────────────────────────────────────
  467. ; Convert_half is for copying 8bit data from dataseg to dmabuffer with 16bit
  468. ; values (16bitvalue= 8bit value*256)
  469. ; one call - convert one buffer half
  470. ; next call - convert the other buffer half
  471. ; ... 2B Continued ...
  472. ;──────────────────────────────────────────────────────────────────────────────
  473. CONVERT_HALF:
  474.            mov     cx,DMABUFFERSIZE/2            ; half buffer size in bytes
  475.            mov     di,[DMABufferDOSOFS]
  476.            cmp     [part],0
  477.            je      not2nd
  478.            add     di,cx
  479. not2nd:    shr     cx,1                        ; count of words in half buffer
  480.            mov     ax,[DMABufferDOSSEG]
  481.            mov     es,ax
  482.            mov     si,offset samplebuffer
  483.            add     si,[position]
  484.            xor     al,al
  485. cloop:     mov     ah,ds:[si]
  486.            stosw
  487.            inc     si
  488.            cmp     si,offset samplebuffer + samplebufferlength-2
  489.            ja      samplerestart
  490.            loop    cloop
  491.            jmp     afterloop
  492. samplerestart:
  493.            ; restart sample
  494.            mov     si,offset samplebuffer
  495.            loop    cloop
  496. afterloop:
  497.            sub     si,offset samplebuffer
  498.            mov     [position],si
  499.            neg     [part]
  500.            inc     [part]      ; part = 1-part  result: 0,1,0,1,0,1,...
  501.            RET
  502.  
  503. END     __start
  504.